/*
 * kernel/sched.c
 *
 * last modified by ChenJie at 2007.07.15
 *
 */
#include <clinux/kernel.h>
#include <clinux/sched.h>
#include <clinux/mm.h>
#include <clinux/time.h>
#include <asm/i8259.h>
#include <asm/i8253.h>
#include <asm/i386.h>

//定义任务数组:
struct task_struct * task[NR_TASKS];

//当前运行任务：
struct task_struct * current_task;
//最后一次使用协处理器的任务:
struct task_struct * last_task_used_math;

//从开机开始的滴答数：
unsigned long volatile jiffies = 0;

//时钟中断入口点：
extern void clock_interrupt(void);

//系统调用中断处理程序:
extern void system_call(void);

//分配的上一个进程号：
long last_pid=0;


/*
 * 功能：
 * 	设置初始任务
 * 输入：
 * 	无
 * 输出：
 * 	无
 * 全局变量：
 * 	init_task
 * 	gdt
 * 	pg_dir
 * 	task
 *
 * 处理说明：
 *	设定当前运行状态是Task0的ring0级:
 *	1.设置ldt
 *	2.设置tss中的静态字段, 动态字段在任务切换时会自动更新
 *	3.在gdt中分别填入ldt和tss的选择子
 *	4.在进程表中注册该任务, 并设定运行指针指向本任务
 *	5.加载ldt选择子到ldtr, 加载tss选择子到tr
 */
static void set_init_task(void)
{
	//设置进程基本信息：
	init_task.task.pid = 0; last_pid = 0;
	init_task.task.state = TASK_RUNNING;
	init_task.task.counter = TASK_INIT_COUNTER;
	init_task.task.priority = TASK_KERNEL_PRIORITY;

	init_task.task.exit_code = 0;
	init_task.task.father = -1;
	
	init_task.task.utime = 0;
	init_task.task.stime = 0;
	init_task.task.cutime = 0;
	init_task.task.cstime = 0;

	init_task.task.start_time = jiffies;
	
	init_task.task.used_math = 0;

	//设置进程LDT表：
	/*
	 * 注意这里task0的ring3级被限制在0~640KB范围内
	 */
	set_seg_desc(&(init_task.task.ldt[0]),0,0,0);
	set_seg_desc(&(init_task.task.ldt[1]),//USER_CS
			DA_CR | DA_DPL3, 
			TASK_TEXT_START, (TASK0_TEXT_LIMIT)>>12);
	set_seg_desc(&(init_task.task.ldt[2]),//USER_DS
			DA_DRW | DA_DPL3,
			TASK_DATA_START, (TASK0_DATA_LIMIT)>>12);
	set_seg_desc(&(init_task.task.ldt[3]),//USER_SS
			DA_DRW | DA_DPL3,
			TASK_STACK_START, (TASK0_STACK_LIMIT)>>12);
	//设置TSS(仅设置动态字段集)：
	init_task.task.tss.back_link = 0;
	init_task.task.tss.esp0 = (unsigned long)(&(init_task.kernel_stack[1024]));
	init_task.task.tss.ss0 = KERNEL_SS;
	init_task.task.tss.esp1 = 0;
	init_task.task.tss.ss1 = 0;
	init_task.task.tss.esp2 = 0;
	init_task.task.tss.ss2 = 0;
	init_task.task.tss.cr3 = (unsigned long)pg_dir;
	init_task.task.tss.es  = 0;
	init_task.task.tss.cs  = 0;
	init_task.task.tss.ss  = 0;
	init_task.task.tss.ds  = 0;
	init_task.task.tss.fs  = 0;
	init_task.task.tss.gs  = 0;
	init_task.task.tss.ldt = TASK_LDT_SELECTOR(0);
	init_task.task.tss.trace_bitmap = 0;
	//init_task.task.tss.i387 = {0,0...};

	//设置GDT中的本任务的LDT描述符和TSS描述符：
	set_tss_desc(&gdt[TASK_TSS_ENTRY(0)], &init_task.task.tss);
	set_ldt_desc(&gdt[TASK_LDT_ENTRY(0)], &init_task.task.ldt);

	//设置映射页表：(直接使用内核页面)
	copy_kernel_pages_to_user(0, 0xA0000, TASK_TEXT_START);
	copy_kernel_pages_to_user(0, 0xA0000, TASK_DATA_START);
	copy_kernel_pages_to_user(0, 0xA0000, TASK_STACK_START);
/*		pg_dir[(TASK_TEXT_START>>22)] = pg_dir[0];
		pg_dir[(TASK_DATA_START>>22)] = pg_dir[0];
		pg_dir[(TASK_STACK_START>>22)] = pg_dir[0];
*/
	//在进程表中注册该任务：
	task[0] = &init_task.task;
	
	//加载LDT和TSS段描述符选择子：
	lldt(TASK_LDT_SELECTOR(0));
	ltr(TASK_TSS_SELECTOR(0));
	//刷新cr3:
	lcr3(pg_dir);
}

/*
 * 功能:
 *	系统调用返回时用于是否进行进程调度的判定以及执行
 * 输入:
 * 	无
 * 输出:
 *	无
 * 处理:
 * 	1.若当前任务的状态不是TASK_RUNNING, 则进行任务调度.
 *	2.若当前任务的时间片已经用完, 则进行任务调度.
 */
void check_schedule(void)
{
	if(current_task->state != TASK_RUNNING 
		|| current_task->counter == 0){
		schedule();
	}
}
/*
 * 功能:
 * 	进程调度函数
 * 输入:
 * 	无
 * 输出:
 * 	无
 * 全局变量:
 * 	task
 * 处理说明:
 * 	1.遍历task数组, 查找处理TASK_RUNNING状态的时间片未用完的有效任务.
 * 	2.若存在这样的任务, 则切换之.
 * 	3.若不存在这样的任务, 则对所有TASK_RUNNING状态的任务赋于时间片.然后
 * 	  切换到任务0开始运行.
 * 调度策略归纳:
 * 	task任务数组中的编号低的TASK_RUNNING任务优先运行.
 */	
void schedule(void)
{
	unsigned long i;
	for(i=0;i<NR_TASKS;i++){
		if(task[i] && task[i]->state == TASK_RUNNING 
			&& task[i]->counter)
			break;
	}
	if(i==NR_TASKS){//进程时间片都用完了
		for(i=0;i<NR_TASKS;i++){
			if(task[i] && task[i]->state == TASK_RUNNING)
				task[i]->counter = task[i]->priority
					+ TASK_INIT_COUNTER;
		}	
		i = 0;
	}
	task_switch_to(i);
}

/*
 * 功能:
 * 	时钟中断的C处理函数
 * 输入:
 * 	cpl		被中断任务的运行级,ring0 or ring3?
 * 输出:
 * 	无
 * 全局变量:
 * 	current_task	当前运行的任务指针
 * 处理说明:
 * 	1.若被中断任务处于ring0, 则结束中断返回任务.
 * 	2.将任务的时间片计数减1, 若时间片未用完, 则结束中断返回任务.
 * 	3.当前任务时间片用完, 转任务调试.
 */
void do_clock_interrupt(int cpl)
{
	static unsigned long times=0;
	current_task->counter--;
	if(current_task->counter) //时间片未用完，返回
		return;
	showtime();
	xycprintk(50,24,TEXT_FR_I|TEXT_FR_G,
			"[system]: jiffies = %ld, pid=%d", 
			jiffies, current_task->pid);
	if(cpl==0) return;//工作在内核态时，直接返回
	times++;
	//xycprintk(158,(times-1)%25,TEXT_FR_I|TEXT_FR_G," ");
	xycprintk(158,times%25,TEXT_FR_I|TEXT_FR_G,"%d", current_task->pid);
	schedule();
}

/* 
 * 功能:
 * 	进程管理初始化
 * 输入:
 * 	无
 * 输出:
 * 	无
 * 全局变量:
 * 	current_task
 * 处理说明:
 * 	1.设置task0
 * 	2.设置当前任务为task0
 *	3.set NT=0, 防止递归任务的麻烦
 *	4.设置IRQ_CLOCK中断处理程序
 *	5.初始化i8253
 *	6.允许IRQ_CLOCK.
 */
void sched_init(void)
{
	set_init_task();
	current_task = &init_task.task;
	//复位NT标志位
	__asm__("pushfl; andl $0xffffbfff, (%esp); popfl");
	set_intr_gate(&idt[IRQ_CLOCK], &clock_interrupt);
	/* 
	 * 设置时钟中断的产生速率是100Hz/s
	 */
	i8253_init(0,3,LATCH);
	enable_irq_clock();
	/* 
	 * 设置系统调用门
	 */
	set_system_gate(&idt[0x80], &system_call);
}
